package com.stone.starter.core.runner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;

import java.util.concurrent.Executors;

/**
 * 抽象公用CommandLineRunner
 *
 * @author Mr_wenpan@163.com 2021/07/15 19:45
 */
public abstract class AbstractCommonCommandLineRunner<T> implements CommandLineRunner {

    /**
     * 是否关闭
     */
    private volatile Boolean isShutdown = false;

    private final Logger LOG = LoggerFactory.getLogger(AbstractCommonCommandLineRunner.class);

    @Override
    public void run(String... args) {
        Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
        Executors.defaultThreadFactory().newThread(this::iRun).start();
    }

    /**
     * 具体执行任务的方法，交给子类实现
     *
     * @author Mr_wenpan@163.com 2021/7/15 7:48 下午
     */
    public void iRun() {
        // 没有关闭就一直执行
        while (!isShutdown) {
            try {
                // 如果获取的数据为空，则休眠一会儿
                T data = getData();
                if (isEmpty(data)) {
                    sleep(getSleepMillis());
                    continue;
                }

                try {
                    // 执行任务
                    executeTask(data);
                } catch (Exception ex) {
                    // 错误补偿
                    LOG.error("后台线程执行任务出现错误，线程名称：[{}]，异常信息：{}", Thread.currentThread().getName(), ex);
                }

            } catch (Exception ex) {
                LOG.error("后台线程执行出现错误，线程名称：[{}]，异常信息：{}", Thread.currentThread().getName(), ex);
            }
        }
    }


    protected abstract void executeTask(T data);

    /**
     * 数据是否为空的判断，满足什么条件为空交给子类去自己实现
     *
     * @return boolean
     * @author Mr_wenpan@163.com 2021/7/15 7:58 下午
     */
    protected abstract boolean isEmpty(T data);

    /**
     * 获取数据的方法
     *
     * @return T
     * @author Mr_wenpan@163.com 2021/7/15 7:51 下午
     */
    protected abstract T getData();

    /**
     * 没有数据时休眠时长
     *
     * @return long
     * @author Mr_wenpan@163.com 2021/7/15 8:02 下午
     */
    protected abstract long getSleepMillis();

    public void shutdown() {
        isShutdown = true;
    }

    /**
     * 没有数据时线程休眠
     */
    private void sleep(Long sleepMillis) {
        try {
            Thread.sleep(sleepMillis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
